<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <style>
      .root,
      .parent,
      .son {
        padding: 50px;
      }

      .son {
        width: 100px;
        height: 100px;
        background-color: green;
      }

      .parent {
        background-color: pink;
      }

      .root {
        background-color: aqua;
      }
    </style>
  </head>

  <body>
    <!-- 
      依赖注入：
        概念：就是通过 provide 与 inject 来实现组件数据的深层传递，将数据传递给后代组件，来解决 props 逐级透传问题。

      inject 注入的逻辑：
        找父组件要，如果有则直接使用，如果没有则往上查找（父亲的父亲），一直这样操作。最终找到根组件。
        如果根组件也没有提供该数据的话，则得到一个 undefined。且报一个警告。


      provide 提供的数据默认不是响应式的, 如果期望变成响应式如何弄：
        Vue3中：
          1. 可以提供一个 computed 下去
        
        Vue2 与 Vue3 都可以使用的
          1. 提供一个函数下去
          2. 直接将整个 this 提供下去
            ElementUI 这些组件库中就是这种玩法




      inject 的语法：

        1. 数组语法
          inject: [数据1, 数据2]

        2. 对象语法
          inject: {
            message: {
              from: 'message', // 当与原注入名同名时，这个属性是可选的
              default: 'default value'
            },
            user: {
              // 对于非基础类型数据，如果创建开销比较大，或是需要确保每个组件实例
              // 需要独立数据的，请使用工厂函数
              default: () => ({ name: 'John' })
            }
          }
      
      provide 的语法：

        1. 对象语法：
          provide: {
            key1: value1
            key2: value2
          }

          PS: 这里获取不了 this，如果需要使用 this 的话，则换成函数写法

        2. 函数返回对象语法：
          provide() {

            return {
              key1: value1,
              key2: value2
            }
          }

     -->

    <div id="app"></div>

    <script type="module">
      import { createApp, computed } from "./lib/vue3.esm.browser.js";

      const app = createApp({
        data() {
          return {
            name: "张",
          };
        },

        /**
         * 提供商
         */
        provide() {
          return {
            // name: this.name,
            name: computed(() => this.name),

            getName() {
              return this.name;
            },

            root: this,
          };
        },

        template: `
          <div class="root">
            <p>Root - <input v-model="name" /></p> 
            <hr />

            <Parent />
          </div>
        `,
      });

      app.component("Parent", {
        inject: ["name", "getName", "root"],

        provide: {
          name: "hello",
        },

        template: `
          <div class="parent">
            <p>Parent - {{ name }} - {{ getName() }} - {{ root.name }}</p>

            <hr />

            <Son/>
          </div>
        `,
      });

      app.component("Son", {
        // inject: ["name", "age"],

        inject: {
          abc: {
            from: "name",
          },
          age: {
            from: "age",
            default: 19,
          },
          root: {
            from: "root",
          },
        },

        template: `
          <div class="son">
            <p>Son</p>  
            <p>Name: {{ abc }}</p>
            <p>Age: {{ age }}</p>
            <p>RootName: {{root.name}}</p>
          </div>
        `,
      });

      app.mount("#app");
    </script>
  </body>
</html>
